package com.sutternow.xwork.interceptors;

import java.io.StringWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.util.ValueStack;

/** 
 * The aim of this Interceptor is to set values in the stack/action based on cookie name/values we are interested in.  
 * 
 * An Action's cookie ognl expression can be expressed in the form of a map or list
 * 
 * 
 * In the case of a list, matching cookie names will be matched directly with setters from the action.
 * ex: <param name="cookies">{ 'pageSize'}</param>
 * If a cookie exists with the name "pageSize", its value will be set on a action with a setter of "PageSize" --> setPageSize(int pageSize)
 * 
 * Using a Map expression allows a level of in-direction.  A map entry's key must match the cookie name, but the value of the map entry 
 * is used to look up which setter to set it to.
 * ex: <param name="cookies">#{ 'peopleActionPageSize' : 'pageSize'}</param>
 *  or  <param name="cookies">#{ 'nameOfCookie' : 'nameOfActionProperty'}</param>
 * 
 * 
 * If a cookie exists with the name "peopleActionPageSize", 
 * the value of that cookie will be set on a action with a setter of "PageSize" --> setPageSize(int pageSize)
 * Without this indirection, users could not customize pageSize differently for different results.  
 * e.g. pageSize for people set to 20.  pageSize for Foo set to 50
 * 
 * @author Matthew Payne
 *
 */ 


public class CookieValueInterceptor implements Interceptor {

    
    private static final Log log = LogFactory.getLog(CookieValueInterceptor.class);
    
    private static final String DEFAULT_COOKIE_KEY = "cookies" ;
   
    String cookieKey = DEFAULT_COOKIE_KEY;
    
    
    public void setCookieKey(String cookieKey) {
        this.cookieKey = cookieKey;
    }
    
    
    public void destroy() {
    }

    public void init() {
    }
    
    public String intercept(ActionInvocation invocation) throws Exception {

        final ValueStack stack = ActionContext.getContext().getValueStack();
        HttpServletRequest request = ServletActionContext.getRequest();

        ActionConfig config = invocation.getProxy().getConfig();
        // get the action's parameters
        final Map parameters = config.getParams();

        if (parameters.containsKey(cookieKey)) {

            String cookieExpression = (String) parameters.get(cookieKey);
            Object obj = stack.findValue(cookieExpression);

            if (obj != null) {

                Cookie cookies[] = request.getCookies();

                if (obj instanceof List) {
                    List cookieNames = (List) obj;

                    for (int i = 0; i < cookies.length; i++) {
                        String value = cookies[i].getValue();
                        String name = cookies[i].getName();

                        if (cookieNames != null && cookieNames.contains(name)) {
                            stack.setValue(name, value);
                        }

                    }

                } else if (obj instanceof Map) {

                    Map cookieMap = (Map) obj;
      
                    for (int i = 0; i < cookies.length; i++) {
                        String value = cookies[i].getValue();
                        String name = cookies[i].getName();

                        if (cookieMap != null && cookieMap.containsKey(name)) {
                            String alias = (String) cookieMap.get(name);
                            stack.setValue(alias, value);
                        }

                    }
                }

            }

        }
        
        return invocation.invoke();

    }
     
    
    public static Map parseToMap(String content) {
        Map map = new HashMap();
        //#{"name1":"alias1","name2":"alias2"} 
        String tmp = content.substring(2, content.length()-1);
        //"name1":"alias1","name2":"alias2"
        String[] values =tmp.split(",");
        
        for (int i = 0; i< values.length; i++) {
            //"name1":"alias1"
            String[] pair= values[i].split(":");
            String key=pair[0].replaceAll("\'", "");
            String value=pair[1].replaceAll("\'", "");
            map.put(key, pair);
        }
        
    return map;    
        
    }
    
    public static String[][] parseToArray(String content) {
        String [][] valueArray;
        //#{"name1":"alias1","name2":"alias2"} 
        String tmp = content.substring(2, content.length()-1);
        //"name1":"alias1","name2":"alias2"
        String[] values =tmp.split(",");
        valueArray = new String[values.length][2];
         
        for (int i = 0; i< values.length; i++) {
            //"name1":"alias1"
            String[] pair= values[i].split(":");
            String key=pair[0].replaceAll("\'", "");
            String value=pair[1].replaceAll("\'", "");
            valueArray[i][0] = key;
            valueArray[i][1] = value;
            
        }
        return valueArray;    
    }
    
    public static String getValue(String searchKey, String [][] values) {
      
        for (int i = 0; i< values.length; i++) {
            if (values[i][0].equals(searchKey)) {
                return values[i][1];
            }
        }
        return null;
    }
 
    public static String[][] setValue(String searchKey, String newValue, String [][] values) {
        boolean found=false;
        
        for (int i = 0; i< values.length; i++) {
            if (values[i][0].equals(searchKey)) {
                found=true;
                values[i][1] = newValue;
            }
        }
        
        if (found==false) {
            String [][]tmpArray= new String[values.length+1][2];
            
            for (int i = 0; i< values.length; i++) {
                tmpArray[i][0] = values[i][0];
                tmpArray[i][1] = values[i][1];
            }
           
            tmpArray[values.length][0] = searchKey;
            tmpArray[values.length][1] = newValue;
            //replace old array
            values = tmpArray;
        } 
        
     return values;
    }
 
    
    public static String mapToOgnl(Map map) {
        StringWriter sr = new StringWriter();
        
        Iterator keyItr = map.keySet().iterator();
       sr.append("#{");     
        while (keyItr.hasNext()) {
            Object key = keyItr.next();
            String value = (String)map.get(key);
            sr.append("\'" + key + "\":\""+ value + "\'");
            if (keyItr.hasNext()) {
                sr.append(",");
            }
            
        }
        sr.append("}");
        
     return    sr.toString();
        
    }
    
    public static Object getCookieValue(HttpServletRequest req, String name) {
        Cookie cookies[] = req.getCookies();
        for (int i = 0; i < cookies.length; i++) {
            String cookieName = cookies[i].getName(); if (name.equals(cookieName)) {
                return cookies[i].getValue();
            }
        }
        return null;
    }
   
    
    public static String arrayToOgnl(String [][] values) {
        StringWriter sr = new StringWriter();
       sr.append("#{");     
       for (int i = 0; i< values.length; i++) {
           Object key = values[i][0];
           Object value = values[i][1];
            
           sr.append("\'" + key + "\":\""+ value + "\'");
           if (i < values.length + 1) {
               sr.append(",");
           }
   
       }
        sr.append("}");
        
     return    sr.toString();
        
    }
   
}